--- 模块功能：音频播放.
-- 支持MP3、amr文件播放；
-- 支持本地TTS播放、通话中TTS播放到对端（需要使用支持TTS功能的core软件）
-- @module audio
-- @author openLuat
-- @license MIT
-- @copyright openLuat
-- @release 2018.3.19
require "log"
require "ril"
require "misc"
require "utils"
require "common"
module(..., package.seeall)

-- 线程id,当前状态，mic音量等级
local co, rco, wait, sMicVolume = nil, nil, nil, 4
-- 录音列表,录音路径,当前id
local files, path, index = {}, '/RecDir/rec00', "0"

--注册core上报的rtos.MSG_AUDIO消息的处理函数
rtos.on(rtos.MSG_AUDIO, function(msg)
    for k, v in pairs(msg) do
        log.error("audio.msg:", k, v)
    end
    coroutine.resume(co, msg.play_end_ind)
end)
rtos.on(rtos.MSG_TTS, function(msg)
        -- local tag = {[0] = "idle", [1] = "open", [2] = "play", [3] = "stop"}
        log.info("audio.ttsMsg", msg.type, msg.result)
        -- if tag[msg.type] then coroutine.resume(co, msg.type == 2 and false or tag[msg.type]) end
        coroutine.resume(co, true)
end)
--- 播放音频文件
-- @string type: 可选"MP3","TTS","TTSCC","RECORD"
-- @string str ：路径字符串或TTS字符串,录音文件则为id(例如"0")
-- @number vol : 可选参数,音量范围0-7,默认6
-- @number speed: 可选参数，TTS语速范围0-100,默认50
-- @return boole: 播放成功返回true,失败返回false
-- @return -1 : 播放过程中被主动停止返回-1
-- @usage audio.play("MP3","/ldata/call.mp3")
-- @usageaudio.play("MP3","/ldata/call.mp3",7)
-- @usageaudio.play("TTS","这是一个TTS语音测试",7,50)
function play(type, str, vol, speed)
    assert(coroutine.running(), '该方法必须在任务(sys.taskInit)中使用,详情请点击:http://wiki.openluat.com.')
    local normal = true
    if not co then
        co = coroutine.running()
        audiocore.setvol(tonumber(vol) or 6)
        if type == "MP3" then
            if not io.exists(str) then
                co = nil
                log.error("播放失败,录音文件不存在!", id)
                return false
            end
            if audiocore.play(str) then
                normal = coroutine.yield()
            end
            audiocore.stop()
        elseif type == "TTS" then
            audiocore.openTTS(tonumber(speed) or 50)
            coroutine.yield()
            audiocore.playTTS(common.utf8ToUcs2(str))
            normal = coroutine.yield()
            audiocore.closeTTS()
            coroutine.yield()
        elseif type == "TTSCC" then
            ril.request("AT+QTTS=1")
            ril.request(string.format("AT+QTTS=4," .. common.utf8ToUcs2(str):toHex()))
            normal = coroutine.yield()
            ril.request("AT+QTTS=0")
            coroutine.yield()
        elseif type == "RECORD" then
            local id = tonumber(str) or 0
            if not io.exists(path .. id) then
                co = nil
                log.error("播放失败,录音文件不存在!", id)
                return false
            end
            ril.request("AT+AUDREC=1,0,2," .. id .. "," .. files[tostring(id)])
            if not coroutine.yield() then
                co = nil
                log.error("录音文件不能被播放!", path .. id)
                return false
            end
        end
        co = nil
        return normal and true or -1
    else
        log.error("audio.openTTS:", "播放通道被占用!")
        return false
    end
end
--- 停止当前录音或者停止播放录音
-- @return 不存在录音返回false
-- @usage audio.stopTTS()
function stopRecord()
    if not rco or not wait then
        log.error("未找到录音任务:", index)
        return false
    end
    ril.request("AT+AUDREC=0,0,0," .. index .. "," .. second * 1000)
    return true
end
--- 停止音频播放
-- @string type,"TTS","TTSCC" 或"RECORD"
-- @return 不存在播放通道返回false
-- @usage audio.stopTTS()
function stop(type)
    if co then
        if type == "TTS" then
            audiocore.stopTTS()
        elseif type == "TTSCC" then
            ril.request("AT+QTTS=3")
        elseif type == "RECORD" then
            ril.request("AT+AUDREC=1,0,3," .. index .. "," .. files[index])
        else
            audiocore.stop()
            coroutine.resume(co, false)
        end
        return true
    else
        log.error("audio.stop:", "没有播放通道被启用!", type or "MP3")
        return false
    end
end
ril.regUrc("+QTTS", function(data, prefix)
    if prefix == "+QTTS" then
        local flag = string.match(data, ": *(%d)", string.len(prefix) + 1)
        --停止播放tts
        if flag == "0" then
            coroutine.resume(co, true)
        end
    end
end)
ril.regRsp("+QTTS", function(cmd, success, response, intermediate)
    local prefix = string.match(cmd, "AT(%+%u+%?*)")
    if prefix == "+QTTS" then
        local action = string.match(cmd, "QTTS=(%d)")
        if action == "3" or (not success and action == "2") then
            coroutine.resume(co, false)
        end
    end
end, 0)
--- 设置喇叭音量等级
-- @number vol，音量值为0-7，0为静音
-- @return bool result，设置成功返回true，失败返回false
-- @usage audio.setVolume(7)
function setVolume(vol)
    return audiocore.setvol(tonumber(vol) or 6)
end
--- 设置麦克音量等级
-- @number vol，音量值为0-15，0为静音
-- @return bool result，设置成功返回true,失败返回false
-- @usage audio.setMicVolume(14)
function setMicVolume(vol)
    ril.request("AT+CMIC=" .. audiocore.LOUDSPEAKER .. "," .. vol)
    return true
end
ril.regRsp("+CMIC", function(cmd, success)
    if success then
        sMicVolume = tonumber(cmd:match("CMIC=%d+,(%d+)"))
    end
end)
--- 获取麦克音量等级
-- @return number vol，麦克音量等级
-- @usage audio.getMicVolume()
function getMicVolume(vol)
    return sMicVolume
end
--- 开始录音
-- @number id: 录音文件的id，范围0-4
-- @number sec:  录音时长，单位：秒
-- @return string: 录音文件的字节流
-- @usage result = record.record(1,10,7)
function record(id, sec, vol)
    assert(coroutine.running(), '该方法必须在任务(sys.taskInit)中使用,详情请点击:http://wiki.openluat.com.')
    id, sec, vol = tonumber(id) or 0, tonumber(sec) or 50, tonumber(vol) or 4
    id, sec, vol = id > 4 and 4 or id, sec > 50 and 50 or sec, vol > 15 and 15 or vol
    setMicVolume(vol)
    if rco then
        log.error("当前录音通道被占用:", rco)
        return false
    end
    rco, wait = coroutine.running(), "record"
    index, second = tostring(id), sec
    ril.request("AT+AUDREC=0,0,1," .. id .. "," .. sec * 1000)
    if not coroutine.yield() then
        rco, wait = nil, nil
        log.error("录音失败,正在通话或空间不足!剩余空间:", rtos.get_fs_free_size())
        return false
    else
        rco, wait = nil, nil
        return io.readFile(path .. id)
    end
end
--- 读取录音数据
-- @number id: 录音文件的id，范围0-4
-- @return data 录音数据字节流
-- @usage data = record.getStream()
function getStream(id)
    return io.readFile(path .. (tonumber(id) or 0))
end
--- 读取录音文件总长度，录音时长
-- @number id: 录音文件的id，范围0-4
-- @return fileSize 录音文件大小
-- @return duration 录音时长
-- @usage fileSize, duration = record.getSize()
function getSize(id)
    local size, duration = io.fileSize(path .. id), 0
    if size > 6 then
        duration = ((size - 6) - ((size - 6) % 1600)) / 1600
    end
    return size, duration
end
--- 删除录音
-- @number id: 录音文件的id，范围0-4
-- @return nil
-- @usage record.remove(0)
function remove(id)
    if files[tostring(id)] then
        files[tostring(id)] = nil
        os.remove(path .. id)
    end
end
--- 删除录音
-- @number id: 录音文件的id，范围0-4
-- @return table: 当天存储的录音id和时长集合
-- @usage record.remove(0)
function getList()
    return files
end
ril.regUrc("+AUDREC", function(data)
    local action, size = data:match("(%d),(%d+)")
    size = tonumber(size)
    if action and size then
        if action == "1" then
            if size > 0 then files[index] = size end
            coroutine.resume(rco, size > 0)
        --录音播放相关
        elseif action == "2" then
            coroutine.resume(co, size > 0)
        end
    end
end)
ril.regRsp("+AUDREC", function(cmd, success)
    local action = cmd:match("AUDREC=%d,%d,(%d)")
    if action == "1" then -- 开始录音
        if not success then
            coroutine.resume(rco, false)
        end
    -- elseif action == '0' then -- 停止录音
    elseif action == "3" then --停止播放录音
        coroutine.resume(co, true)
    end
end)
--默认音频通道设置为LOUDSPEAKER，因为目前的模块只支持LOUDSPEAKER通道
audiocore.setchannel(audiocore.LOUDSPEAKER)
--默认MIC音量等级设置为1级，最低为0级，最高为15级
setMicVolume(sMicVolume)
